#ifndef JFLAGS_H
#define JFLAGS_H

#include <initializer_list>
#include <type_traits>

template<typename Enum>
class JFlags
{
	// 模板必须是枚举类型
	static_assert((std::is_enum<Enum>::value),
	"JFlags is only usable on enumeration types.");

	// 枚举类型最大只能接受sizeof(int)大小的数据
	static_assert((sizeof(Enum) <= sizeof(int)),
	"JFlags uses an int as storage, so an enum with underlying long long will overflow.");

public:
	typedef Enum  enum_type;

	constexpr inline JFlags() noexcept : i(0) {}
	constexpr inline JFlags(Enum f) noexcept : i(f) {}
	constexpr inline JFlags(const JFlags &other) : i(other.i) {}

	constexpr inline JFlags(std::initializer_list<Enum> flags) noexcept
		: i(initializer_list_helper(flags.begin(), flags.end())) {}

	const inline JFlags &operator=(const JFlags &other)
	{ i = other.i; return *this; }

public:
	const inline JFlags &operator&=(int mask) noexcept
	{ i &= mask; return *this; }

	const inline JFlags &operator&=(unsigned int mask) noexcept
	{ i &= mask; return *this; }

	const inline JFlags &operator&=(Enum mask) noexcept
	{ i &= mask; return *this; }

	const inline JFlags &operator|=(JFlags f) noexcept
	{ i |= f.i; return *this; }

	const inline JFlags &operator|=(Enum f) noexcept
	{ i |= f; return *this; }

	const inline JFlags &operator^=(JFlags f) noexcept
	{ i ^= f.i; return *this; }

	const inline JFlags &operator^=(Enum f) noexcept
	{ i ^= f; return *this; }

	constexpr inline operator int() const noexcept
	{ return i; }

	constexpr inline JFlags operator|(JFlags f) const noexcept
	{ return JFlags( static_cast<Enum>(i | f.i) ); }

	constexpr inline JFlags operator|(Enum f) const noexcept
	{ return JFlags( static_cast<Enum>(i | f) ); }

	constexpr inline JFlags operator^(JFlags f) const noexcept
	{ return JFlags( static_cast<Enum>(i ^ f.i) ); }

	constexpr inline JFlags operator^(Enum f) const noexcept
	{ return JFlags( static_cast<Enum>(i ^ f) ); }

	constexpr inline JFlags operator&(int mask) const noexcept
	{ return JFlags( static_cast<Enum>(i & mask)); }

	constexpr inline JFlags operator&(unsigned int mask) const noexcept
	{ return JFlags( static_cast<Enum>(i & mask)); }

	constexpr inline JFlags operator&(Enum f) const noexcept
	{ return JFlags( static_cast<Enum>(i & f) ); }

	constexpr inline JFlags operator~() const noexcept
	{ return JFlags( static_cast<Enum>(~i) ); }

	constexpr inline bool operator!() const noexcept
	{ return !i; }

	constexpr inline bool testFlag(Enum f) const noexcept
	{ return (i & f) == f and (f != 0 or i == f); }

	constexpr inline JFlags &setFlag(Enum f, bool on = true) const noexcept
	{ return on ? (*this |= f) : (*this &= ~f); }

private:
	constexpr static inline int
	initializer_list_helper(typename std::initializer_list<Enum>::const_iterator it,
							typename std::initializer_list<Enum>::const_iterator end) noexcept
	{
		return (it == end ? 0 : (*it | initializer_list_helper(it + 1, end)));
	}
	int i;
};

/*------------------------------------------------------------*/

#define J_DECLARE_FLAGS(Flags, Enum)  typedef JFlags<Enum> Flags;

#define J_DECLARE_OPERATORS_FOR_FLAGS(Flags)               \
	constexpr inline JFlags<Flags::enum_type> operator|     \
	(Flags::enum_type f1, Flags::enum_type f2) noexcept      \
	{ return JFlags<Flags::enum_type>(f1) | f2; }             \
	constexpr inline JFlags<Flags::enum_type> operator|        \
	(Flags::enum_type f1, JFlags<Flags::enum_type> f2) noexcept \
	{ return f2 | f1; }


#endif //JFLAGS_H
